#include "wx/wxprec.h"
#include "wx/toplevel.h"
#include "wx/toolbar.h"
#include "wx/gtk/private.h"
#include "wx/evtloop.h"
#include "wx/event.h"
#include <gtk/gtk.h>
#include <gdk/gdkx.h>
#include "wx/gtk/win_gtk.h"
#include "wx/unix/utilsx11.h"
#include <X11/Xatom.h>

extern int g_openDialogs;
extern wxWindow *g_delayedFocus;
static wxTopLevelWindowGTK *g_activeFrame = ( wxTopLevelWindowGTK* ) NULL;
static wxTopLevelWindowGTK *g_lastActiveFrame = ( wxTopLevelWindowGTK* ) NULL;
static int g_sendActivateEvent = -1;

extern "C" {
  static void wxgtk_window_set_urgency_hint( GtkWindow *win,
      gboolean setting ) {
    wxASSERT_MSG( GTK_WIDGET_REALIZED( win ), wxT( "wxgtk_window_set_urgency_hint: GdkWindow not realized" ) );
    GdkWindow *window = GTK_WIDGET( win )->window;
    XWMHints *wm_hints;
    wm_hints = XGetWMHints( GDK_WINDOW_XDISPLAY( window ), GDK_WINDOW_XWINDOW( window ) );
    if( !wm_hints ) {
      wm_hints = XAllocWMHints();
    }
    if( setting ) {
      wm_hints->flags |= XUrgencyHint;
    } else {
      wm_hints->flags &= ~XUrgencyHint;
    }
    XSetWMHints( GDK_WINDOW_XDISPLAY( window ), GDK_WINDOW_XWINDOW( window ), wm_hints );
    XFree( wm_hints );
  }

  static gboolean gtk_frame_urgency_timer_callback( wxTopLevelWindowGTK *win ) {
    #if GTK_CHECK_VERSION(2,7,0)
    if( !gtk_check_version( 2, 7, 0 ) ) {
      gtk_window_set_urgency_hint( GTK_WINDOW( win->m_widget ), FALSE );
    } else
    #endif
      wxgtk_window_set_urgency_hint( GTK_WINDOW( win->m_widget ), FALSE );
    win->m_urgency_hint = -2;
    return FALSE;
  }
}
#define WXUNUSED(identifier)
extern "C" {
  static gboolean gtk_frame_focus_in_callback( GtkWidget *widget, GdkEvent * WXUNUSED( event ), wxTopLevelWindowGTK *win ) {
    switch( g_sendActivateEvent ) {
      case -1:
        g_sendActivateEvent = 1;
        break;
      case 0:
        g_sendActivateEvent = -1;
        break;
    }
    g_activeFrame = win;
    g_lastActiveFrame = g_activeFrame;
    switch( win->m_urgency_hint ) {
      default:
        g_source_remove( win->m_urgency_hint );
      case -1:
        if( !gtk_check_version( 2, 7, 0 ) ) {
          gtk_window_set_urgency_hint( GTK_WINDOW( widget ), FALSE );
        } else {
          wxgtk_window_set_urgency_hint( GTK_WINDOW( widget ), FALSE );
        }
        win->m_urgency_hint = -2;
        break;
      case -2:
        break;
    }
    wxLogTrace( wxT( "activate" ), wxT( "Activating frame %p (from focus_in)" ), g_activeFrame );
    wxActivateEvent event( wxEVT_ACTIVATE, true, g_activeFrame->GetId() );
    event.SetEventObject( g_activeFrame );
    g_activeFrame->GetEventHandler()->ProcessEvent( event );
    return FALSE;
  }
}

extern "C" {
  static gboolean gtk_frame_focus_out_callback( GtkWidget *widget, GdkEventFocus * gdk_event, wxTopLevelWindowGTK *win ) {
    g_sendActivateEvent = 0;
    if( g_activeFrame ) {
      wxLogTrace( wxT( "activate" ), wxT( "Activating frame %p (from focus_in)" ), g_activeFrame );
      wxActivateEvent event( wxEVT_ACTIVATE, false, g_activeFrame->GetId() );
      event.SetEventObject( g_activeFrame );
      g_activeFrame->GetEventHandler()->ProcessEvent( event );
      g_activeFrame = NULL;
    }
    return FALSE;
  }
}

extern "C" {
  static gboolean gtk_frame_focus_callback( GtkWidget * ( widget ),
      GtkDirectionType( d ),
      wxWindow * ( win ) ) {
    if( g_isIdle ) {
      wxapp_install_idle_handler();
    }
    return TRUE;
  }
}

extern "C" {
  static void gtk_frame_size_callback( GtkWidget * ( widget ), GtkAllocation* alloc, wxTopLevelWindowGTK *win ) {
    if( g_isIdle ) {
      wxapp_install_idle_handler();
    }
    if( !win->m_hasVMT ) {
      return;
    }
    if( ( win->m_width != alloc->width ) || ( win->m_height != alloc->height ) ) {
      win->m_width = alloc->width;
      win->m_height = alloc->height;
      win->GtkUpdateSize();
    }
  }
}

extern "C" {
  static gboolean
  gtk_frame_delete_callback( GtkWidget * ( widget ), GdkEvent * ( event ), wxTopLevelWindowGTK *win ) {
    if( win->IsEnabled() &&
        ( g_openDialogs == 0 || ( win->GetExtraStyle() & wxTOPLEVEL_EX_DIALOG ) ||
          win->IsGrabbed() ) ) {
      win->Close();
    }
    return TRUE;
  }
}

extern "C" {
  static gboolean
  gtk_frame_configure_callback( GtkWidget * ( widget ), GdkEventConfigure * ( event ), wxTopLevelWindowGTK *win ) {
    if( !win->m_hasVMT || !win->IsShown() ) {
      return FALSE;
    }
    int x = 0;
    int y = 0;
    gdk_window_get_root_origin( win->m_widget->window, &x, &y );
    win->m_x = x;
    win->m_y = y;
    wxMoveEvent mevent( wxPoint( win->m_x, win->m_y ), win->GetId() );
    mevent.SetEventObject( win );
    win->GetEventHandler()->ProcessEvent( mevent );
    return FALSE;
  }
}

extern "C" {
  static void
  gtk_frame_realized_callback( GtkWidget * ( widget ), wxTopLevelWindowGTK *win ) {
    if( g_isIdle ) {
      wxapp_install_idle_handler();
    }
    gdk_window_set_decorations( win->m_widget->window, ( GdkWMDecoration )win->m_gdkDecor );
    gdk_window_set_functions( win->m_widget->window, ( GdkWMFunction )win->m_gdkFunc );
    if( ( win->m_gdkFunc & GDK_FUNC_RESIZE ) == 0 ) {
      gtk_window_set_resizable( GTK_WINDOW( win->m_widget ), FALSE );
    } else {
      gtk_window_set_policy( GTK_WINDOW( win->m_widget ), 1, 1, 1 );
    }
    wxIconBundle iconsOld = win->GetIcons();
    if( iconsOld.GetIcon( -1 ).Ok() ) {
      win->SetIcon( wxNullIcon );
      win->SetIcons( iconsOld );
    }
  }
}

extern "C" {
  static gboolean
  gtk_frame_map_callback( GtkWidget * ( widget ), GdkEvent * ( event ), wxTopLevelWindow *win ) {
    win->SetIconizeState( false );
    if( win->wxWindowBase::Show( true ) ) {
      wxShowEvent eventShow( win->GetId(), true );
      eventShow.SetEventObject( win );
      win->GetEventHandler()->ProcessEvent( eventShow );
    }
    return false;
  }
}

extern "C" {
  static gboolean
  gtk_frame_unmap_callback( GtkWidget * widget, GdkEvent * event, wxTopLevelWindow *win ) {
    win->SetIconizeState( true );
    return false;
  }
}

extern "C" {
  static gboolean
  gtk_window_expose_callback( GtkWidget *widget, GdkEventExpose *gdk_event, wxWindow *win ) {
    GtkPizza *pizza = GTK_PIZZA( widget );
    gtk_paint_flat_box( win->m_widget->style, pizza->bin_window, GTK_STATE_NORMAL, GTK_SHADOW_NONE,
                        &gdk_event->area, win->m_widget, ( char * )"base", 0, 0, -1, -1 );
    return FALSE;
  }
}

static void wxInsertChildInTopLevelWindow( wxTopLevelWindowGTK* parent, wxWindow* child ) {
  wxASSERT( GTK_IS_WIDGET( child->m_widget ) );
  if( !parent->m_insertInClientArea ) {
    wxTopLevelWindowGTK* frame = ( wxTopLevelWindowGTK* ) parent;
    gtk_pizza_put( GTK_PIZZA( frame->m_mainWidget ), GTK_WIDGET( child->m_widget ), child->m_x, child->m_y, child->m_width, child->m_height );
  } else {
    gtk_pizza_put( GTK_PIZZA( parent->m_wxwindow ), GTK_WIDGET( child->m_widget ), child->m_x, child->m_y, child->m_width, child->m_height );
  }
}

void wxTopLevelWindowGTK::Init() {
  m_sizeSet = false;
  m_miniEdge = 0;
  m_miniTitle = 0;
  m_mainWidget = ( GtkWidget* ) NULL;
  m_insertInClientArea = true;
  m_isIconized = false;
  m_fsIsShowing = false;
  m_fsSaveFlag = 0;
  m_themeEnabled = true;
  m_gdkDecor = m_gdkFunc = 0;
  m_grabbed = false;
  m_urgency_hint = -2;
}

bool wxTopLevelWindowGTK::Create( wxWindow *parent, wxWindowID id, const wxString& title, const wxPoint& pos,
                                  const wxSize& sizeOrig, long style, const wxString &name ) {
  wxSize size = sizeOrig;
  size.x = WidthDefault( size.x );
  size.y = HeightDefault( size.y );
  wxTopLevelWindows.Append( this );
  m_needParent = false;
  if( !PreCreation( parent, pos, size ) ||
      !CreateBase( parent, id, pos, size, style, wxDefaultValidator, name ) ) {
    wxFAIL_MSG( wxT( "wxTopLevelWindowGTK creation failed" ) );
    return false;
  }
  m_title = title;
  m_insertCallback = ( wxInsertChildFunction ) wxInsertChildInTopLevelWindow;
  if( m_widget == NULL ) {
    if( GetExtraStyle() & wxTOPLEVEL_EX_DIALOG ) {
      m_widget = gtk_window_new( GTK_WINDOW_TOPLEVEL );
      gtk_window_set_type_hint( GTK_WINDOW( m_widget ), GDK_WINDOW_TYPE_HINT_DIALOG );
      gtk_window_set_position( GTK_WINDOW( m_widget ), GTK_WIN_POS_CENTER_ON_PARENT );
    } else {
      m_widget = gtk_window_new( GTK_WINDOW_TOPLEVEL );
      if( !gtk_check_version( 2, 1, 0 ) ) {
        if( style & wxFRAME_TOOL_WINDOW ) {
          gtk_window_set_type_hint( GTK_WINDOW( m_widget ), GDK_WINDOW_TYPE_HINT_UTILITY );
          style |= wxFRAME_NO_TASKBAR;
        }
      }
    }
  }
  wxWindow *topParent = wxGetTopLevelParent( m_parent );
  if( topParent && ( ( ( GTK_IS_WINDOW( topParent->m_widget ) ) &&
                       ( GetExtraStyle() & wxTOPLEVEL_EX_DIALOG ) ) ||
                     ( style & wxFRAME_FLOAT_ON_PARENT ) ) ) {
    gtk_window_set_transient_for( GTK_WINDOW( m_widget ),
                                  GTK_WINDOW( topParent->m_widget ) );
  }
  #if GTK_CHECK_VERSION(2,2,0)
  if( !gtk_check_version( 2, 2, 0 ) ) {
    if( style & wxFRAME_NO_TASKBAR ) {
      gtk_window_set_skip_taskbar_hint( GTK_WINDOW( m_widget ), TRUE );
    }
  }
  #endif
  if( !gtk_check_version( 2, 4, 0 ) ) {
    if( style & wxSTAY_ON_TOP ) {
      gtk_window_set_keep_above( GTK_WINDOW( m_widget ), TRUE );
    }
  }
  if( style & wxMAXIMIZE ) {
    gtk_window_maximize( GTK_WINDOW( m_widget ) );
  }
  gtk_window_set_title( GTK_WINDOW( m_widget ), wxGTK_CONV( title ) );
  GTK_WIDGET_UNSET_FLAGS( m_widget, GTK_CAN_FOCUS );
  g_signal_connect( m_widget, "delete_event", G_CALLBACK( gtk_frame_delete_callback ), this );
  m_mainWidget = gtk_pizza_new();
  gtk_widget_show( m_mainWidget );
  GTK_WIDGET_UNSET_FLAGS( m_mainWidget, GTK_CAN_FOCUS );
  gtk_container_add( GTK_CONTAINER( m_widget ), m_mainWidget );
  if( m_miniEdge == 0 ) { // wxMiniFrame has its own version.
    g_signal_connect( m_mainWidget, "expose_event", G_CALLBACK( gtk_window_expose_callback ), this );
  }
  m_wxwindow = gtk_pizza_new();
  gtk_widget_show( m_wxwindow );
  gtk_container_add( GTK_CONTAINER( m_mainWidget ), m_wxwindow );
  GTK_WIDGET_UNSET_FLAGS( m_wxwindow, GTK_CAN_FOCUS );
  if( m_parent ) {
    m_parent->AddChild( this );
  }
  g_signal_connect( m_widget, "size_allocate", G_CALLBACK( gtk_frame_size_callback ), this );
  PostCreation();
  if( ( m_x != -1 ) || ( m_y != -1 ) ) {
    gtk_widget_set_uposition( m_widget, m_x, m_y );
  }
  gtk_window_set_default_size( GTK_WINDOW( m_widget ), m_width, m_height );
  //  we cannot set MWM hints and icons before the widget has
  //  been realized, so we do this directly after realization
  g_signal_connect( m_widget, "realize", G_CALLBACK( gtk_frame_realized_callback ), this );
  // map and unmap for iconized state
  g_signal_connect( m_widget, "map_event", G_CALLBACK( gtk_frame_map_callback ), this );
  g_signal_connect( m_widget, "unmap_event", G_CALLBACK( gtk_frame_unmap_callback ), this );
  // the only way to get the window size is to connect to this event
  g_signal_connect( m_widget, "configure_event", G_CALLBACK( gtk_frame_configure_callback ), this );
  // disable native tab traversal
  g_signal_connect( m_widget, "focus", G_CALLBACK( gtk_frame_focus_callback ), this );
  // activation
  g_signal_connect_after( m_widget, "focus_in_event", G_CALLBACK( gtk_frame_focus_in_callback ), this );
  g_signal_connect_after( m_widget, "focus_out_event", G_CALLBACK( gtk_frame_focus_out_callback ), this );
  // decorations
  if( ( style & wxSIMPLE_BORDER ) || ( style & wxNO_BORDER ) ) {
    m_gdkDecor = 0;
    m_gdkFunc = GDK_FUNC_MOVE;
    if( ( style & wxMINIMIZE_BOX ) == wxMINIMIZE_BOX ) {
      m_gdkFunc |= GDK_FUNC_MINIMIZE;
    }
    if( ( style & wxCLOSE_BOX ) == wxCLOSE_BOX ) {
      m_gdkFunc |= GDK_FUNC_CLOSE;
    }
  } else if( m_miniEdge > 0 ) {
    m_gdkDecor = 0;
    m_gdkFunc = 0;
    if( ( style & wxRESIZE_BORDER ) != 0 ) {
      m_gdkFunc |= GDK_FUNC_RESIZE;
    }
  } else {
    m_gdkDecor = ( long ) GDK_DECOR_BORDER;
    m_gdkFunc = ( long ) GDK_FUNC_MOVE;
    if( ( style & wxCAPTION ) != 0 ) {
      m_gdkDecor |= GDK_DECOR_TITLE;
    }
    if( ( style & wxCLOSE_BOX ) != 0 ) {
      m_gdkFunc |= GDK_FUNC_CLOSE;
    }
    if( ( style & wxSYSTEM_MENU ) != 0 ) {
      m_gdkDecor |= GDK_DECOR_MENU;
    }
    if( ( style & wxMINIMIZE_BOX ) != 0 ) {
      m_gdkFunc |= GDK_FUNC_MINIMIZE;
      m_gdkDecor |= GDK_DECOR_MINIMIZE;
    }
    if( ( style & wxMAXIMIZE_BOX ) != 0 ) {
      m_gdkFunc |= GDK_FUNC_MAXIMIZE;
      m_gdkDecor |= GDK_DECOR_MAXIMIZE;
    }
    if( ( style & wxRESIZE_BORDER ) != 0 ) {
      m_gdkFunc |= GDK_FUNC_RESIZE;
      m_gdkDecor |= GDK_DECOR_RESIZEH;
    }
  }
  return true;
}

wxTopLevelWindowGTK::~wxTopLevelWindowGTK() {
  if( m_grabbed ) {
    wxFAIL_MSG( _T( "Window still grabbed" ) );
    RemoveGrab();
  }
  m_isBeingDeleted = true;
  if( GTK_IS_WINDOW( m_widget ) ) {
    gtk_window_set_focus( GTK_WINDOW( m_widget ), NULL );
  }
  if( g_activeFrame == this ) {
    g_activeFrame = NULL;
  }
  if( g_lastActiveFrame == this ) {
    g_lastActiveFrame = NULL;
  }
}

bool wxTopLevelWindowGTK::EnableCloseButton( bool enable ) {
  if( enable ) {
    m_gdkFunc |= GDK_FUNC_CLOSE;
  } else {
    m_gdkFunc &= ~GDK_FUNC_CLOSE;
  }
  if( GTK_WIDGET_REALIZED( m_widget ) && ( m_widget->window ) ) {
    gdk_window_set_functions( m_widget->window, ( GdkWMFunction )m_gdkFunc );
  }
  return true;
}

static void wxAddAccelerators( wxList& accelEntries, wxMenu* menu ) {
  size_t i;
  for( i = 0; i < menu->GetMenuItems().GetCount(); i++ ) {
    wxMenuItem* item = ( wxMenuItem* ) menu->GetMenuItems().Item( i )->GetData();
    if( item->GetSubMenu() ) {
      wxAddAccelerators( accelEntries, item->GetSubMenu() );
    } else if( !item->GetItemLabel().IsEmpty() ) {
      wxAcceleratorEntry* entry = wxAcceleratorEntry::Create( item->GetItemLabel() );
      if( entry ) {
        entry->Set( entry->GetFlags(), entry->GetKeyCode(), item->GetId() );
        accelEntries.Append( ( wxObject* ) entry );
      }
    }
  }
}

static wxAcceleratorTable wxCreateAcceleratorTableForMenuBar( wxMenuBar* menuBar ) {
  wxList accelEntries;
  size_t i;
  for( i = 0; i < menuBar->GetMenuCount(); i++ ) {
    wxAddAccelerators( accelEntries, menuBar->GetMenu( i ) );
  }
  size_t n = accelEntries.GetCount();
  if( n == 0 ) {
    return wxAcceleratorTable();
  }
  wxAcceleratorEntry* entries = new wxAcceleratorEntry[n];
  for( i = 0; i < accelEntries.GetCount(); i++ ) {
    wxAcceleratorEntry* entry = ( wxAcceleratorEntry* ) accelEntries.Item( i )->GetData();
    entries[i] = ( *entry );
    delete entry;
  }
  wxAcceleratorTable table( n, entries );
  delete[] entries;
  return table;
}

bool wxTopLevelWindowGTK::ShowFullScreen( bool show, long style ) {
  if( show == m_fsIsShowing ) {
    return false;
  } // return what?
  if( show ) {
    wxFrame* frame = wxDynamicCast( this, wxFrame );
    if( frame ) {
      if( frame->GetMenuBar() ) {
        wxAcceleratorTable table( wxCreateAcceleratorTableForMenuBar( frame->GetMenuBar() ) );
        if( table.IsOk() ) {
          SetAcceleratorTable( table );
        }
      }
      #if wxUSE_TOOLBAR
      if( frame->GetToolBar() && frame->GetToolBar()->IsShown() ) {
        frame->GetToolBar()->Show( false );
      }
      #endif
    }
  }
  #if wxUSE_TOOLBAR
  else {
    wxFrame* frame = wxDynamicCast( this, wxFrame );
    if( frame && frame->GetToolBar() ) {
      frame->GetToolBar()->Show( true );
    }
  }
  #endif
  m_fsIsShowing = show;
  wxX11FullScreenMethod method =
    wxGetFullScreenMethodX11( ( WXDisplay* )GDK_DISPLAY(),
                              ( WXWindow )GDK_ROOT_WINDOW() );
  #if GTK_CHECK_VERSION(2,2,0)
  if( ( method == wxX11_FS_WMSPEC ) && !gtk_check_version( 2, 2, 0 ) ) {
    if( show ) {
      m_fsSaveFlag = style;
      gtk_window_fullscreen( GTK_WINDOW( m_widget ) );
    } else {
      m_fsSaveFlag = 0;
      gtk_window_unfullscreen( GTK_WINDOW( m_widget ) );
    }
  } else
  #endif // GTK+ >= 2.2.0
  {
    GdkWindow *window = m_widget->window;
    if( show ) {
      m_fsSaveFlag = style;
      GetPosition( &m_fsSaveFrame.x, &m_fsSaveFrame.y );
      GetSize( &m_fsSaveFrame.width, &m_fsSaveFrame.height );
      int screen_width, screen_height;
      wxDisplaySize( &screen_width, &screen_height );
      gint client_x, client_y, root_x, root_y;
      gint width, height;
      if( method != wxX11_FS_WMSPEC ) {
        // don't do it always, Metacity hates it
        m_fsSaveGdkFunc = m_gdkFunc;
        m_fsSaveGdkDecor = m_gdkDecor;
        m_gdkFunc = m_gdkDecor = 0;
        gdk_window_set_decorations( window, ( GdkWMDecoration )0 );
        gdk_window_set_functions( window, ( GdkWMFunction )0 );
      }
      gdk_window_get_origin( m_widget->window, &root_x, &root_y );
      gdk_window_get_geometry( m_widget->window, &client_x, &client_y, &width, &height, NULL );
      gdk_window_move_resize( m_widget->window, -client_x, -client_y, screen_width + 1, screen_height + 1 );
      wxSetFullScreenStateX11( ( WXDisplay* )GDK_DISPLAY(), ( WXWindow )GDK_ROOT_WINDOW(), ( WXWindow )GDK_WINDOW_XWINDOW( window ),
                               show, &m_fsSaveFrame, method );
    } else { // hide
      m_fsSaveFlag = 0;
      if( method != wxX11_FS_WMSPEC ) {
        // don't do it always, Metacity hates it
        m_gdkFunc = m_fsSaveGdkFunc;
        m_gdkDecor = m_fsSaveGdkDecor;
        gdk_window_set_decorations( window, ( GdkWMDecoration )m_gdkDecor );
        gdk_window_set_functions( window, ( GdkWMFunction )m_gdkFunc );
      }
      wxSetFullScreenStateX11( ( WXDisplay* )GDK_DISPLAY(), ( WXWindow )GDK_ROOT_WINDOW(), ( WXWindow )GDK_WINDOW_XWINDOW( window ),
                               show, &m_fsSaveFrame, method );
      SetSize( m_fsSaveFrame.x, m_fsSaveFrame.y, m_fsSaveFrame.width, m_fsSaveFrame.height );
    }
  }
  if( show && !IsShown() ) {
    Show();
  }
  return true;
}

bool wxTopLevelWindowGTK::Show( bool show ) {
  wxASSERT_MSG( ( m_widget != NULL ), wxT( "invalid frame" ) );
  if( show == IsShown() ) {
    return false;
  }
  if( show && !m_sizeSet ) {
    GtkOnSize();
  }
  wxTopLevelWindowBase::Show( show );
  if( !show ) {
    gtk_window_move( ( GtkWindow* )m_widget, m_x, m_y );
  }
  return true;
}

void wxTopLevelWindowGTK::Raise() {
  gtk_window_present( GTK_WINDOW( m_widget ) );
}

void wxTopLevelWindowGTK::DoMoveWindow( int ( x ), int ( y ), int ( width ), int ( height ) ) {
  wxFAIL_MSG( wxT( "DoMoveWindow called for wxTopLevelWindowGTK" ) );
}

void wxTopLevelWindowGTK::DoSetSize( int x, int y, int width, int height, int sizeFlags ) {
  wxASSERT_MSG( ( m_widget != NULL ), wxT( "invalid frame" ) );
  if( m_resizing ) {
    return;
  }
  m_resizing = true;
  int old_x = m_x;
  int old_y = m_y;
  int old_width = m_width;
  int old_height = m_height;
  if( ( sizeFlags & wxSIZE_ALLOW_MINUS_ONE ) == 0 ) {
    if( x != -1 ) {
      m_x = x;
    }
    if( y != -1 ) {
      m_y = y;
    }
  } else {
    m_x = x;
    m_y = y;
  }
  if( width != -1 ) {
    m_width = width;
  }
  if( height != -1 ) {
    m_height = height;
  }
  int minWidth = GetMinWidth(),
      minHeight = GetMinHeight(),
      maxWidth = GetMaxWidth(),
      maxHeight = GetMaxHeight();
  if( ( minWidth != -1 ) && ( m_width < minWidth ) ) {
    m_width = minWidth;
  }
  if( ( minHeight != -1 ) && ( m_height < minHeight ) ) {
    m_height = minHeight;
  }
  if( ( maxWidth != -1 ) && ( m_width > maxWidth ) ) {
    m_width = maxWidth;
  }
  if( ( maxHeight != -1 ) && ( m_height > maxHeight ) ) {
    m_height = maxHeight;
  }
  if( ( m_x != -1 ) || ( m_y != -1 ) ) {
    if( ( m_x != old_x ) || ( m_y != old_y ) ) {
      gtk_window_move( GTK_WINDOW( m_widget ), m_x, m_y );
    }
  }
  if( ( m_width != old_width ) || ( m_height != old_height ) ) {
    gtk_window_resize( GTK_WINDOW( m_widget ), m_width, m_height );
    m_sizeSet = false;
  }
  m_resizing = false;
}

void wxTopLevelWindowGTK::DoGetClientSize( int *width, int *height ) const {
  wxASSERT_MSG( ( m_widget != NULL ), wxT( "invalid frame" ) );
  if( height ) {
    *height = m_height - 2 * m_miniEdge - m_miniTitle;
    if( *height < 0 ) {
      *height = 0;
    }
  }
  if( width ) {
    *width = m_width - 2 * m_miniEdge;
    if( *width < 0 ) {
      *width = 0;
    }
  }
}

void wxTopLevelWindowGTK::DoSetClientSize( int width, int height ) {
  wxASSERT_MSG( ( m_widget != NULL ), wxT( "invalid frame" ) );
  DoSetSize( -1, -1, width + m_miniEdge * 2, height  + m_miniEdge * 2 + m_miniTitle, 0 );
}

void wxTopLevelWindowGTK::DoSetSizeHints( int minW, int minH,
    int maxW, int maxH,
    int incW, int incH ) {
  wxTopLevelWindowBase::DoSetSizeHints( minW, minH, maxW, maxH, incW, incH );
  if( m_widget ) {
    int minWidth = GetMinWidth(),
        minHeight = GetMinHeight(),
        maxWidth = GetMaxWidth(),
        maxHeight = GetMaxHeight();
    gint flag = 0;
    GdkGeometry geom;
    if( ( minWidth != -1 ) || ( minHeight != -1 ) ) {
      flag |= GDK_HINT_MIN_SIZE;
    }
    if( ( maxWidth != -1 ) || ( maxHeight != -1 ) ) {
      flag |= GDK_HINT_MAX_SIZE;
    }
    geom.min_width = minWidth;
    geom.min_height = minHeight;
    geom.max_width = ( maxHeight == -1 ) ? maxWidth : ( maxWidth == -1 ) ? wxGetDisplaySize().GetWidth() : maxWidth ;
    geom.max_height = ( maxWidth == -1 ) ? maxHeight : ( maxHeight == -1 ) ? wxGetDisplaySize().GetHeight() : maxHeight ;
    gtk_window_set_geometry_hints( GTK_WINDOW( m_widget ), ( GtkWidget* ) NULL, &geom, ( GdkWindowHints ) flag );
  }
}

void wxTopLevelWindowGTK::GtkOnSize() {
  if( m_resizing ) {
    return;
  }
  m_resizing = true;
  if( m_wxwindow == NULL ) {
    return;
  }
  int minWidth = GetMinWidth(), minHeight = GetMinHeight(), maxWidth = GetMaxWidth(), maxHeight = GetMaxHeight();
  if( ( minWidth != -1 ) && ( m_width < minWidth ) ) {
    m_width = minWidth;
  }
  if( ( minHeight != -1 ) && ( m_height < minHeight ) ) {
    m_height = minHeight;
  }
  if( ( maxWidth != -1 ) && ( m_width > maxWidth ) ) {
    m_width = maxWidth;
  }
  if( ( maxHeight != -1 ) && ( m_height > maxHeight ) ) {
    m_height = maxHeight;
  }
  if( m_mainWidget ) {
    int client_x = m_miniEdge;
    int client_y = m_miniEdge + m_miniTitle;
    int client_w = m_width - 2 * m_miniEdge;
    int client_h = m_height - 2 * m_miniEdge - m_miniTitle;
    if( client_w < 0 ) {
      client_w = 0;
    }
    if( client_h < 0 ) {
      client_h = 0;
    }
    gtk_pizza_set_size( GTK_PIZZA( m_mainWidget ), m_wxwindow, client_x, client_y, client_w, client_h );
  } else {
  }
  m_sizeSet = true;
  wxSizeEvent event( wxSize( m_width, m_height ), GetId() );
  event.SetEventObject( this );
  GetEventHandler()->ProcessEvent( event );
  m_resizing = false;
}

void wxTopLevelWindowGTK::OnInternalIdle() {
  if( !m_sizeSet && GTK_WIDGET_REALIZED( m_wxwindow ) ) {
    GtkOnSize();
    if( g_isIdle ) {
      wxapp_install_idle_handler();
    }
    return;
  }
  if( GTK_WIDGET_REALIZED( m_wxwindow ) ) {
    if( g_delayedFocus &&
        wxGetTopLevelParent( ( wxWindow* )g_delayedFocus ) == this ) {
      wxLogTrace( _T( "focus" ), _T( "Setting focus from wxTLW::OnIdle() to %s(%s)" ),
                  g_delayedFocus->GetClassInfo()->GetClassName(),
                  g_delayedFocus->GetLabel().c_str() );
      g_delayedFocus->SetFocus();
      g_delayedFocus = NULL;
    }
  }
  wxWindow::OnInternalIdle();
  if( g_sendActivateEvent != -1 ) {
    bool activate = g_sendActivateEvent != 0;
    g_sendActivateEvent = -1;
    wxTheApp->SetActive( activate, ( wxWindow * )g_lastActiveFrame );
  }
}

void wxTopLevelWindowGTK::SetTitle( const wxString &title ) {
  wxASSERT_MSG( ( m_widget != NULL ), wxT( "invalid frame" ) );
  if( title == m_title ) {
    return;
  }
  m_title = title;
  gtk_window_set_title( GTK_WINDOW( m_widget ), wxGTK_CONV( title ) );
}

void wxTopLevelWindowGTK::SetIcon( const wxIcon &icon ) {
  SetIcons( wxIconBundle( icon ) );
}

void wxTopLevelWindowGTK::SetIcons( const wxIconBundle &icons ) {
  wxASSERT_MSG( ( m_widget != NULL ), wxT( "invalid frame" ) );
  wxTopLevelWindowBase::SetIcons( icons );
  if( !GTK_WIDGET_REALIZED( m_widget ) ) {
    return;
  }
  GList *list = NULL;
  size_t max = icons.m_icons.GetCount();
  for( size_t i = 0; i < max; i++ ) {
    if( icons.m_icons[i].Ok() ) {
      list = g_list_prepend( list, icons.m_icons[i].GetPixbuf() );
    }
  }
  gtk_window_set_icon_list( GTK_WINDOW( m_widget ), list );
  g_list_free( list );
}

void wxTopLevelWindowGTK::Maximize( bool maximize ) {
  if( maximize ) {
    gtk_window_maximize( GTK_WINDOW( m_widget ) );
  } else {
    gtk_window_unmaximize( GTK_WINDOW( m_widget ) );
  }
}

bool wxTopLevelWindowGTK::IsMaximized() const {
  if( !m_widget->window ) {
    return false;
  }
  return gdk_window_get_state( m_widget->window ) & GDK_WINDOW_STATE_MAXIMIZED;
}

void wxTopLevelWindowGTK::Restore() {
  gtk_window_present( GTK_WINDOW( m_widget ) );
}

void wxTopLevelWindowGTK::Iconize( bool iconize ) {
  if( iconize ) {
    gtk_window_iconify( GTK_WINDOW( m_widget ) );
  } else {
    gtk_window_deiconify( GTK_WINDOW( m_widget ) );
  }
}

bool wxTopLevelWindowGTK::IsIconized() const {
  return m_isIconized;
}

void wxTopLevelWindowGTK::SetIconizeState( bool iconize ) {
  if( iconize != m_isIconized ) {
    m_isIconized = iconize;
    ( void )SendIconizeEvent( iconize );
  }
}

void wxTopLevelWindowGTK::AddGrab() {
  if( !m_grabbed ) {
    m_grabbed = true;
    gtk_grab_add( m_widget );
    wxEventLoop().Run();
    gtk_grab_remove( m_widget );
  }
}

void wxTopLevelWindowGTK::RemoveGrab() {
  if( m_grabbed ) {
    gtk_main_quit();
    m_grabbed = false;
  }
}

static bool do_shape_combine_region( GdkWindow* window, const wxRegion& region ) {
  if( window ) {
    if( region.IsEmpty() ) {
      gdk_window_shape_combine_mask( window, NULL, 0, 0 );
    } else {
      gdk_window_shape_combine_region( window, region.GetRegion(), 0, 0 );
      return true;
    }
  }
  return false;
}

bool wxTopLevelWindowGTK::SetShape( const wxRegion& region ) {
  wxCHECK_MSG( HasFlag( wxFRAME_SHAPED ), false,
               _T( "Shaped windows must be created with the wxFRAME_SHAPED style." ) );
  GdkWindow *window = NULL;
  if( m_wxwindow ) {
    window = GTK_PIZZA( m_wxwindow )->bin_window;
    do_shape_combine_region( window, region );
  }
  window = m_widget->window;
  return do_shape_combine_region( window, region );
}

bool wxTopLevelWindowGTK::IsActive() {
  return ( this == ( wxTopLevelWindowGTK* )g_activeFrame );
}

void wxTopLevelWindowGTK::RequestUserAttention( int flags ) {
  bool new_hint_value = false;
  ::wxYieldIfNeeded();
  if( m_urgency_hint >= 0 ) {
    g_source_remove( m_urgency_hint );
  }
  m_urgency_hint = -2;
  if( GTK_WIDGET_REALIZED( m_widget ) && !IsActive() ) {
    new_hint_value = true;
    if( flags & wxUSER_ATTENTION_INFO ) {
      m_urgency_hint = g_timeout_add( 5000, ( GSourceFunc )gtk_frame_urgency_timer_callback, this );
    } else {
      m_urgency_hint = -1;
    }
  }
  #if GTK_CHECK_VERSION(2,7,0)
  if( !gtk_check_version( 2, 7, 0 ) ) {
    gtk_window_set_urgency_hint( GTK_WINDOW( m_widget ), new_hint_value );
  } else
  #endif
    wxgtk_window_set_urgency_hint( GTK_WINDOW( m_widget ), new_hint_value );
}

void wxTopLevelWindowGTK::SetWindowStyleFlag( long style ) {
  #if defined(__WXGTK24__) || GTK_CHECK_VERSION(2,2,0)
  // Store which styles were changed
  long styleChanges = style ^ m_windowStyle;
  #endif
  wxWindow::SetWindowStyleFlag( style );
  if( !m_widget ) {
    return;
  }
  if( ( styleChanges & wxSTAY_ON_TOP ) && !gtk_check_version( 2, 4, 0 ) ) {
    gtk_window_set_keep_above( GTK_WINDOW( m_widget ), m_windowStyle & wxSTAY_ON_TOP );
  }
  #if GTK_CHECK_VERSION(2,2,0)
  if( ( styleChanges & wxFRAME_NO_TASKBAR ) && !gtk_check_version( 2, 2, 0 ) ) {
    gtk_window_set_skip_taskbar_hint( GTK_WINDOW( m_widget ), m_windowStyle & wxFRAME_NO_TASKBAR );
  }
  #endif // GTK+ 2.2
}

#include <X11/Xlib.h>

static Window wxGetTopmostWindowX11( Display *dpy, Window child ) {
  Window root, parent;
  Window* children;
  unsigned int nchildren;
  XQueryTree( dpy, child, &root, &parent, &children, &nchildren );
  XFree( children );
  while( parent != root ) {
    child = parent;
    XQueryTree( dpy, child, &root, &parent, &children, &nchildren );
    XFree( children );
  }
  return child;
}

bool wxTopLevelWindowGTK::SetTransparent( wxByte alpha ) {
  if( !m_widget || !m_widget->window ) {
    return false;
  }
  Display* dpy = GDK_WINDOW_XDISPLAY( m_widget->window );
  Window win = wxGetTopmostWindowX11( dpy, GDK_WINDOW_XID( m_widget->window ) );
  if( alpha == 0xff ) {
    XDeleteProperty( dpy, win, XInternAtom( dpy, "_NET_WM_WINDOW_OPACITY", False ) );
  } else {
    long opacity = alpha * 0x1010101L;
    XChangeProperty( dpy, win, XInternAtom( dpy, "_NET_WM_WINDOW_OPACITY", False ), XA_CARDINAL, 32, PropModeReplace,
                     ( unsigned char * ) &opacity, 1L );
  }
  XSync( dpy, False );
  return true;
}

bool wxTopLevelWindowGTK::CanSetTransparent() {
  #if GTK_CHECK_VERSION(2,10,0)
  if( !gtk_check_version( 2, 10, 0 ) ) {
    return ( gtk_widget_is_composited( m_widget ) );
  } else
  #endif
  {
    return false;
  }
}
